package org.cloudfoundry.identity.uaa.mock.mfa_provider;

import org.apache.commons.lang.ArrayUtils;
import org.cloudfoundry.identity.uaa.mfa_provider.GoogleMfaProviderConfig;
import org.cloudfoundry.identity.uaa.mfa_provider.JdbcMfaProviderProvisioning;
import org.cloudfoundry.identity.uaa.mfa_provider.MfaProvider;
import org.cloudfoundry.identity.uaa.mock.InjectedMockContextTest;
import org.junit.Before;
import org.junit.Test;
import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders;
import org.springframework.restdocs.payload.FieldDescriptor;
import org.springframework.restdocs.snippet.Snippet;
import org.springframework.security.oauth2.common.util.RandomValueStringGenerator;

import static org.cloudfoundry.identity.uaa.mfa_provider.MfaProvider.MfaProviderType.GOOGLE_AUTHENTICATOR;
import static org.cloudfoundry.identity.uaa.test.SnippetUtils.fieldWithPath;
import static org.cloudfoundry.identity.uaa.util.JsonUtils.serializeExcludingProperties;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
import static org.springframework.restdocs.payload.JsonFieldType.BOOLEAN;
import static org.springframework.restdocs.payload.JsonFieldType.NUMBER;
import static org.springframework.restdocs.payload.JsonFieldType.STRING;
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

public class MfaProviderEdpointsDocs extends InjectedMockContextTest {
    private static final String NAME_DESC = "Human-readable name for this provider";
    private static final String ACTIVE_DESC = "Defaults to true.";
    private static final String ID_DESC = "Unique identifier for this provider - GUID generated by the UAA";
    private static final String CREATED_DESC = "UAA sets the creation date";
    private static final String LAST_MODIFIED_DESC = "UAA sets the modification date";
    private static final String IDENTITY_ZONE_ID_DESC = "Set to the zone that this provider will be active in. Determined either by the Host header or the zone switch header.";
    //    private static final FieldDescriptor IDENTITY_ZONE_ID = fieldWithPath("identityZoneId").type(STRING).description(IDENTITY_ZONE_ID_DESC);

    private static final FieldDescriptor ACTIVE = fieldWithPath("active").optional(true).type(BOOLEAN).description(ACTIVE_DESC);
    private static final FieldDescriptor NAME = fieldWithPath("name").required().type(STRING).description(NAME_DESC);
    private static final FieldDescriptor CONFIG = fieldWithPath("config").required().description("Configuration of MFA providers");
    private static final FieldDescriptor TYPE = fieldWithPath("type").required().type(STRING).description("Type of MFA providers");
    private static final FieldDescriptor LAST_MODIFIED = fieldWithPath("last_modified").description(LAST_MODIFIED_DESC);
    private static final FieldDescriptor ID = fieldWithPath("id").type(STRING).description(ID_DESC);
    private static final FieldDescriptor CREATED = fieldWithPath("created").description(CREATED_DESC);
    private static final FieldDescriptor IDENTITY_ZONE_ID = fieldWithPath("identityZoneId").type(STRING).description(IDENTITY_ZONE_ID_DESC);
    private FieldDescriptor[] commonProviderFields = {
            NAME,
        //    CONFIG,
            ACTIVE,
            TYPE,
    };
    private String adminToken;
    private JdbcMfaProviderProvisioning mfaProviderProvisioning;

    @Before
    public void setUp() throws Exception {
        adminToken = testClient.getClientCredentialsOAuthAccessToken(
                "admin",
                "adminsecret",
                "");

        mfaProviderProvisioning = getWebApplicationContext().getBean(JdbcMfaProviderProvisioning.class);
    }

    @Test
    public void testCreateGoogleMfaProvider() throws Exception {
        MfaProvider<GoogleMfaProviderConfig> mfaProvider = new MfaProvider<>()
                .setActive(true)
                .setName(new RandomValueStringGenerator(5).generate())
                .setType(GOOGLE_AUTHENTICATOR)
                .setConfig(new GoogleMfaProviderConfig().setProviderDescription("Google MFA for default zone"));

        FieldDescriptor[] idempotentFields = (FieldDescriptor[]) ArrayUtils.addAll(commonProviderFields, new FieldDescriptor[]{
                fieldWithPath("config.algorithm").optional("SHA256").type(STRING).description("Algorithm"),
                fieldWithPath("config.providerDescription").optional(null).type(STRING).description("provider description"),
                fieldWithPath("config.digits").optional(6).type(NUMBER).description("digits"),
                fieldWithPath("config.issuer").optional(null).type(STRING).description("issuer. mention default behavior"),
                fieldWithPath("config.duration").optional(30).type(NUMBER).description("duration")
        });
        Snippet requestFields = requestFields(idempotentFields);

        Snippet responseFields = responseFields((FieldDescriptor[]) ArrayUtils.addAll(idempotentFields, new FieldDescriptor[]{
                ID,
                CREATED,
                LAST_MODIFIED,
                IDENTITY_ZONE_ID
        }));
        getMockMvc().perform(RestDocumentationRequestBuilders.post("/mfa-providers", mfaProvider.getId())
                .accept(APPLICATION_JSON)
                .header("Authorization", "Bearer " + adminToken)
                .contentType(APPLICATION_JSON)
                .content(serializeExcludingProperties(mfaProvider, "id", "created", "last_modified", "identityZoneId")))
                .andExpect(status().isCreated())
                .andDo(document("{ClassName}/{methodName}",
                        preprocessRequest(prettyPrint()),
                        preprocessResponse(prettyPrint()),
                        requestHeaders(
                                headerWithName("Authorization").description("Bearer token containing `zones.<zone id>.admin` or `uaa.admin`(only in the same zone that you are a user of)"),
                                headerWithName("X-Identity-Zone-Id").description("May include this header to administer another zone if using `zones.<zone id>.admin` or `uaa.admin` scope against the default UAA zone.").optional()
                        ),
                        requestFields,
                        responseFields)
                );
    }

}